#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <signal.h>
#include <sys/wait.h>



#include <list>


#define PORT 8014
#define MAX_CLIENT 10
using namespace std;


struct socketList
{
	string ipAddress;
	int socketfd;
	int serverStatus;
};

list<socketList> sockList;
list<socketList>::iterator iter; 
const char *programName;
//to change  correlate the ip address in topolist to socketfd. then each disconnect with help us update the list



union title_length
{
	unsigned int length;
	char byte[2];
}theLength;

void makeDatagram(char* buf, int i);

int main(int argc, char **argv)
{
	sigset_t  sigmask;
	struct sigaction sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sa.sa_handler = SIG_IGN;
	if (sigaction(SIGPIPE, &sa, NULL) < 0)
	 printf("sigaction failed");


	int hasMonitor = 0;

	FILE *fp;

	int i,nready;
	int listen_fd,
		accept_fd,
		connfd,
		sockfd,
		maxfd,
		maxi,
		listen_monitor,
		connect_fd;
	char buffer[1000];
	char buffer1[1000];
	int client[MAX_CLIENT];
	string sTemp;

	fd_set rset, allset;

	listen_monitor = socket(AF_INET,SOCK_STREAM,0);
	listen_fd = socket(AF_INET, SOCK_STREAM,0);
	if(listen_fd < 0){perror("socket"); exit(1);}
	if(listen_monitor < 0){perror("socket"); exit(1);}

	struct sockaddr_in ser_addr, cli_addr,connect_addr;

//collect log information
bzero(&ser_addr,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
ser_addr.sin_port = htons(2627);


//for java monitor
bzero(&connect_addr,sizeof(connect_addr));
connect_addr.sin_family=AF_INET;
connect_addr.sin_port = htons(PORT);
connect_addr.sin_addr.s_addr = htonl(INADDR_ANY);

int on = 1;
if(setsockopt(listen_monitor, SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
{
	perror("set socket option error!!!");
	exit(1);
}

//bind for java monitor

if( bind(listen_monitor, (struct sockaddr *)&connect_addr, sizeof(connect_addr)) < 0 )
{
	perror("connect_fd bind error");
	exit(1);
}

if(listen(listen_monitor,2) < 0)
{
	perror("listen error");
	exit(1);
}

int length = sizeof(connect_addr);

/*
if(hasMonitor == 1)
{//has the java monitor
	if(connect(connect_fd, (struct sockaddr *)&connect_addr, length) < 0){ perror("connect"); exit(1);}
	printf("connection successfully!\n");
}*/

int on1 = 1;
if((setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on1,sizeof(on1))) < 0)
{
	perror("set socket option error! (for log collection)");
	exit(1);
}


//bind for log collection
if( bind(listen_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0 )
{
	perror("bind error");
	exit(1);
}

if(listen(listen_fd,MAX_CLIENT) < 0)
{
	perror("listen error");
	exit(1);
}
struct socketList serverSocket;
serverSocket.socketfd = -1;
serverSocket.ipAddress = "128.112.139.97";
serverSocket.serverStatus = 0;
sockList.insert(sockList.end(),serverSocket);

maxi = -1;
//for(i=0; i<MAX_CLIENT;i++) client[i] = -1;
for(iter = sockList.begin(); iter != sockList.end();iter++)
{
	(*iter).socketfd = -1;
}
FD_ZERO(&rset);
maxfd = listen_fd > listen_monitor ? listen_fd:listen_monitor;



while(1)
{
	//rset = allset;
	printf("start proxy!!!\n");
	FD_SET(listen_fd, &rset);
	if(hasMonitor == 0) FD_SET(listen_monitor,&rset);
	if(hasMonitor == 1) FD_SET(connect_fd, &rset);
	for(iter = sockList.begin(); iter != sockList.end();iter++)
		{
		if(iter->socketfd != -1)
			{
				FD_SET(iter->socketfd,&rset);
				//cout<<"set "<<iter->ipAddress<<"and sockfd:"<<iter->socketfd<<"to rset"<<maxfd<<endl;
			}
		}
	printf("nready before is: %d\n",nready);
	nready = select(maxfd+1, &rset, NULL, NULL, NULL);
	printf("nready: %d\n",nready);

	if(FD_ISSET(listen_monitor,&rset))
		{//new java monitor connection
			connect_fd = accept(listen_monitor, NULL, NULL);
			FD_CLR(listen_monitor, &rset);
			FD_SET(connect_fd, &rset);
			hasMonitor = 1;
			makeDatagram(buffer,1);
			printf("the ip: %s\n", buffer+3);//0x01 lengthserverIP;peer1IP;peer2IP;...

			if(hasMonitor == 1)
			{//send the new topolist to server
				printf("strlen: %d,,,%d\n",strlen(buffer),strlen(buffer+3));
				if(send(connect_fd,buffer,strlen(buffer+3)+3,0)<0){perror("send");exit(1);}
			}
			if(connect_fd > maxfd)
				maxfd = connect_fd;
			if(--nready <= 0)
				continue;
		}
	if(hasMonitor == 1)
		{
		
	if(FD_ISSET(connect_fd,&rset))
		{//new datagram from java monitor
			printf("control message\n");
		
			int n;
				if( (n = read(connect_fd, buffer,1000 )) <= 0 )
				{
					printf("java monitor disconnect\n");
					//handle disconnect event
				//related the i with the ip address in the list, delete it in the list, also open a new socket wait for accpeting control info.
				close(connect_fd);
				FD_CLR(connect_fd, &rset);//delete it in set
				//if(maxfd == connect_fd);
				//--maxfd;
				FD_SET(listen_monitor,&rset);
				hasMonitor = 0;
				}
				else
				{
					printf("in else\n");
					buffer[n-1] = 0;
					if(buffer[0] == 0x06)
						{//close peer 0x06 length IP
							//find the socket descriptor in sockList
							//cout<<"type 06"<<buffer+3
							//<<"\t size:"<<strlen(buffer+3)<<endl;
							string sTemp;
							sTemp.assign(buffer+3);
							
							for(iter = sockList.begin(); iter != sockList.end();iter++)
								{
									if((*iter).ipAddress == sTemp) 
										{
											printf("find the ip address\n");
											break;
										}
								}
							if(iter != sockList.end())
								{
									printf("we have the ip address\n");
									makeDatagram(buffer,6);
									printf("strlen: %d,,,%d\n",strlen(buffer),strlen(buffer+3));
									if(send((*iter).socketfd,buffer,strlen(buffer+3)+3,0)<0){perror("send to peer");exit(1);}
									(*iter).serverStatus = 0;
									//cout<<"server Status:"<<iter->serverStatus<<endl;
									makeDatagram(buffer,1);
									if(hasMonitor == 1)
									{
									if(send(connect_fd,buffer,strlen(buffer+3)+3,0)<0){perror("send to monitor");exit(1);}
									}
									//0x08 send the response
								}
							else printf("there is no such ip\n");
						}
					if(buffer[0] == 0x07)
						{//start peer
						//cout<<"type 07"<<endl;
						string sTemp;
						sTemp.assign(buffer+3);
						for(iter = sockList.begin(); iter != sockList.end();iter++)
						{
								//cout<<"buffer2: "<<buffer+3<<"\tsTemp:"<<sTemp<<endl;
									//cout<<"sTemp's length:"<<sTemp.length()<<"\t ipAddress's length"<<(iter->ipAddress).length()<<endl;
									//cout<<"sTemp == iter->ipAddress"<<(strcmp(sTemp.c_str(),iter->ipAddress.c_str()))<<endl;
									//cout<<"(*iter).ipAddress:"<<iter->ipAddress<<endl;
								if((*iter).ipAddress == sTemp) 
								{
									printf("find the ip address\n");
									break;
								}
						}
						if(iter != sockList.end())
							{
							printf("we have the ip address\n");
							if(iter->serverStatus == 1){//the server already started do nothing
							}
							else{
								makeDatagram(buffer,7);
								printf("strlen: %d,,,%d\n",strlen(buffer),strlen(buffer+3));
									if(send((*iter).socketfd,buffer,strlen(buffer+3)+3,0)<0)
									{
										perror("send to socket");
										exit(1);
									}
								(*iter).serverStatus = 1;
								}
								makeDatagram(buffer,1);
								if(hasMonitor == 1)
								{
								if(send(connect_fd,buffer,strlen(buffer+3)+3,0)<0){perror("send to monitor");exit(1);}
								}
							//0x09 send the response
							}
						}
					//FD_SET(connect_fd, &rset);
				}
				
				printf("buffer3: %s\t n:%d\n", buffer,n);
				printf("finished!!!!!\n");
				
				printf("here nready is : %d\n",nready);
printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
				if(--nready <= 0)
				continue;
		}
}
	if(FD_ISSET(listen_fd, &rset))
	{//for new connection
		printf("new connection\n");
		int client_addr = sizeof(cli_addr);
		connfd = accept(listen_fd, (struct sockaddr *)&cli_addr, (socklen_t*)&client_addr);
		//there is a new connection, get its IP information
		int n = read(connfd, buffer, 1000);//get its IP information, because of ssh tunnel, we cannot get peer's ip
		buffer[n] = 0;
		printf("we get ip address: %s\n", buffer);
		//sTemp == buffer;
		
		sTemp.assign(buffer); //save its ip information
		//printf("stemp : %s\n", sTemp.c_str());
		printf("stemp == 128.112.139.97: %d\n", (sTemp == "128.112.139.97"));
		//so insert it into topo list
		if(sTemp == "128.112.139.97")
			{
				printf("in if sTemp == 128.112.139.97");
				for(iter = sockList.begin(); iter != sockList.end(); iter++)
					{
					//cout<<"iter->ipAddress"<<iter->ipAddress<<endl;
					if(iter->ipAddress == sTemp)
						{
						printf("set status to 1\n");
						iter->serverStatus =1;
						iter->socketfd = connfd;
						}
					}
			}
		if(sTemp != "128.112.139.97") //new peer connection!!
		{
			int n = 0;
			for(iter = sockList.begin(); iter != sockList.end(); iter++)
			{//to see if it is an existed ip
				//cout<<"sTemp::"<<sTemp<<"\titer i::"<<(*iter).ipAddress<<endl;
				if(sTemp == (*iter).ipAddress) n = 1,printf("the ip exists\n");// exists the same peer
			}
			
			
			if (n == 0)//no existed peer in the list so add new peer
			{//save the new ipAddress and socket descriptor
				struct socketList newConnectSocket;
				newConnectSocket.socketfd = connfd;
				newConnectSocket.ipAddress = sTemp;
				newConnectSocket.serverStatus = 1;
				//cout<<"new socket address :"<<newConnectSocket.ipAddress<<endl;
				sockList.insert(sockList.end(),newConnectSocket);//insert into list
			}
			}
		
		makeDatagram(buffer,1);
		printf("the ip: %s\n", buffer+3);//0x01 lengthserverIP;peer1IP;peer2IP;...
		int i = 0;
		for(iter = sockList.begin(); iter != sockList.end(); iter++,i++)
		{
			 printf("%d the ip we have: %s \t sockfd: %d\n", i, (*iter).ipAddress.c_str(),iter->socketfd);
		}
		if(hasMonitor == 1)
		{//send the new topolist to server
			printf("strlen: %d,,,%d\n",strlen(buffer),strlen(buffer+3));
			if(send(connect_fd,buffer,strlen(buffer+3)+3,0)<0){perror("send");exit(1);}
		}
		if(sockList.size() == FD_SETSIZE)
				perror("too many");
		FD_SET(connfd, &rset);
		if(connfd > maxfd)
				maxfd = connfd;
		if (sockList.size() > maxi)
				maxi = i;
printf("........................................................\n");
		if(--nready <= 0)
				continue;
printf("........................................................\n");
/*
		for(i = 0; i < MAX_CLIENT; i++)
			if(client[i] < 0)
			{
				client[i] = connfd;
				break;
			}
			
			
			*/
	}
	for (iter = sockList.begin(); iter != sockList.end(); iter++)
	{//handle the input informaion
	printf("handle the input information!!\n");
		if( (sockfd = (*iter).socketfd) < 0 )//check which client[i] has connection
			continue;
		
		printf("socklist.size(): %d\t socketfd: %d\n", sockList.size(), (*iter).socketfd);
		
		if(FD_ISSET(sockfd, &rset))//check the data of the connected sock
		{
		printf("read information\n");
			int n;
			if( (n = read(sockfd, buffer,1000 )) <= 0 )
			{
				printf("a socket disconnect\n");
				//related the i with the ip address in the list, delete it in the list, also open a new socket wait for accpeting control info.
				close(sockfd);
				FD_CLR(sockfd, &rset);//delete it in set
				if(sTemp != "128.112.139.97")
					sockList.erase(iter);//delete it in array
				else
					{iter->socketfd = -1;iter->serverStatus = 0; }
				//send new topo information
				makeDatagram(buffer,1);
				printf("the ip: %s\n", buffer+3);//0x01 lengthserverIP;peer1IP;peer2IP;...
				if(hasMonitor == 1)
				{//send the new topolist to server
					printf("strlen: %d,,,%d\n",strlen(buffer),strlen(buffer+3));
					if(send(connect_fd,buffer,strlen(buffer+3)+3,0)<0){perror("send");exit(1);}
				}
			}
			else 
			{//received information
				buffer[n+1]= '\n';
				buffer[n] = '\r';
				buffer[n+2]=0;
				buffer[1]=3;
				buffer[2]=3;
				if(buffer[0]==0x04||buffer[0]==0x05||buffer[0]==0x02||buffer[0]==0x03)
				{//it is log information
					//write to other the java monitor
					printf("we get the information: %s\n", buffer+3);
					if(hasMonitor == 1)
					{
						if(send(connect_fd,buffer,n+1,0)<0){printf("socket id: %d",connect_fd);perror("send to monitor1");exit(1);}
					}
				}
				if(buffer[0]==0x08)
					{//response to close peer
					}
				if(buffer[0]==0x09)
					{//response to start peer
					}
			}
			
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
			if(--nready <= 0)
				break;

		}
	}
}
close(listen_monitor);
close(listen_fd);
}
void makeDatagram(char* buf, int i)
{
	if(i == 1)//it is for topo
		{

			buf[0] = 0x01;//title
			theLength.length = sockList.size();
			buf[1] = 3;//theLength.byte[1];
			buf[2] = 3;//theLength.byte[0];//length
			string temp;
			for(iter = sockList.begin(); iter != sockList.end(); iter++)
			{
			//cout<<"2 server status:"<<iter->serverStatus<<endl;
				if((*iter).serverStatus == 1)
					{//the running server
						temp +=(*iter).ipAddress+"|";
						printf("temp: %s\r\n", temp.c_str());
					}
			}
			sprintf(buf+3,"%s\r\n",temp.c_str());	
		}
	if(i == 6)
		{
			buf[0] = 0x06;
			buf[1] = 3;
			buf[2] = 7;//length
			string temp = "close";
			sprintf(buf+3,"%s\r\n",temp.c_str());
		}
	if(i==7)
		{
			buf[0] =0x07;
			buf[1] = 3;
			buf[2] = 7;//length
			string temp = "start";
			sprintf(buf+3,"%s\r\n",temp.c_str());
		}
}
